package com.fluidops.datacatalog.reltables.adapter;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.openrdf.model.Literal;
import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.model.ValueFactory;
import org.openrdf.query.BindingSet;
import org.openrdf.query.MalformedQueryException;
import org.openrdf.query.QueryEvaluationException;
import org.openrdf.query.QueryLanguage;
import org.openrdf.query.TupleQuery;
import org.openrdf.query.TupleQueryResult;
import org.openrdf.repository.RepositoryConnection;
import org.openrdf.repository.RepositoryException;
import org.openrdf.repository.RepositoryResult;

import com.fluidops.datacatalog.reltables.data.Vocabulary.DCTERMS;
import com.fluidops.datacatalog.reltables.data.Vocabulary.DataCube;
import com.fluidops.datacatalog.reltables.data.Vocabulary.Eurostat;
import com.fluidops.datacatalog.reltables.data.Vocabulary.SKOS;
import com.fluidops.datacatalog.reltables.util.LRUCache;

public class EurostatExtractor implements ExtractionAdapter{

	private static Logger logger = Logger.getLogger(EurostatExtractor.class.getName());

	private RepositoryConnection schemaConnection;

	private URI schemaContext;

	public static final int CACHE_SIZE = 300;

	private Map<URI, Set<String>> entityLabelCache;
	
	public EurostatExtractor(RepositoryConnection schemaConnection, URI schemaContext) {
		this.schemaConnection = schemaConnection;
		this.schemaContext = schemaContext;
		
		/*
		 * several caches to avoid to much database accesses
		 */
		this.entityLabelCache = new LRUCache<URI, Set<String>>(CACHE_SIZE);
	}

	@Override
	public Set<String> retrieveDatasetStringLabels(URI identifier){
		Set<String> dsLabels = new TreeSet<String>();

		ValueFactory vf = this.schemaConnection.getValueFactory();
		URI datasetSubject = vf.createURI("http://eurostat.linked-statistics.org/data/", identifier.getLocalName());
		
		RepositoryResult<Statement> result = null;
		try{
			try{
				result = this.schemaConnection.getStatements(datasetSubject, DCTERMS.TITLE, null, false, this.schemaContext);

				Statement s;
				while(result.hasNext()){
					s = result.next();
					dsLabels.add(s.getObject().stringValue());
				}
			}finally{
				if(result != null){
					result.close();
				}
			}
		}catch(RepositoryException e){
			logger.warn("Could not query repository for dataset string labels " + identifier.stringValue() , e);
		}

		return dsLabels;
	}

	@Override
	public List<URI> retrieveEntityURIs(URI datasetIdentifier){
		List<URI> eLabels = new LinkedList<URI>();

		ValueFactory vf = this.schemaConnection.getValueFactory();
		URI datasetSubject = vf.createURI("http://eurostat.linked-statistics.org/data/", datasetIdentifier.getLocalName());
		
		RepositoryResult<Statement> result = null;
		try{
			try{
				result = this.schemaConnection.getStatements(datasetSubject, Eurostat.PROP_GEO , null, false, this.schemaContext);

				Statement s;
				while(result.hasNext()){
					s = result.next();
					if(s.getObject() instanceof URI){
						eLabels.add( (URI) s.getObject());
					}
				}
			}finally{
				if(result != null){
					result.close();
				}
			}
		}catch(RepositoryException e){
			logger.warn("Could not query repository for entity uris " + datasetIdentifier.stringValue() , e);
		}

		return eLabels;
	}

	@Override
	public Set<String> retrieveEntityStringLabels(final URI entityURI){
		if(!this.entityLabelCache.containsKey(entityURI)){
			Set<String> eLabels = new TreeSet<String>();
			RepositoryResult<Statement> result = null;
			try{
				try{
					result = this.schemaConnection.getStatements(entityURI, SKOS.PREFLABEL , null, false, this.schemaContext);

					Statement s;
					while(result.hasNext()){
						s = result.next();
						eLabels.add(s.getObject().stringValue());
					}
					this.entityLabelCache.put(entityURI, eLabels);
				}finally{
					if(result != null){
						result.close();
					}
				}
			}catch(RepositoryException e){
				logger.warn("Could not query repository for dataset string labels " + entityURI.stringValue() , e);
			}
		}
		return this.entityLabelCache.get(entityURI);
	}

	@Override
	public List<URI> retrieveDimensionURIs(URI datasetIdentifier){
		List<URI> labels = new LinkedList<URI>();

		ValueFactory vf = this.schemaConnection.getValueFactory();
		URI datasetSubject = vf.createURI("http://eurostat.linked-statistics.org/data/", datasetIdentifier.getLocalName());
		
		
		RepositoryResult<Statement> result = null;
		try{
			try{
				result = this.schemaConnection.getStatements(datasetSubject, DataCube.DIMENSION , null, false, this.schemaContext);

				Statement s;
				while(result.hasNext()){
					s = result.next();
					if(!s.getObject().equals(Eurostat.PROP_GEO)){
						labels.add((URI) s.getObject());
					}
				}
			}finally{
				if(result != null){
					result.close();
				}
			}
		}catch(RepositoryException e){
			logger.warn("Could not query repository for dataset dimension uris " + datasetIdentifier.stringValue() , e);
		}

		return labels;
	}

	@Override
	public Set<String> retrieveDimensionStringLabels(URI datasetIdentifier, List<URI> dimensions){
		Set<String> dimLabels = new TreeSet<String>();

		for(URI uri : dimensions){
			dimLabels.addAll(retrieveDimensionStringLabels(datasetIdentifier, uri));
		}
		return dimLabels;
	}

	@Override
	public Set<String> retrieveDimensionStringLabels(URI datasetIdentifier, URI dimensions){
		Set<String> dimLabels = new TreeSet<String>();

		ValueFactory vf = this.schemaConnection.getValueFactory();
		URI datasetSubject = vf.createURI("http://eurostat.linked-statistics.org/data/", datasetIdentifier.getLocalName());
		
		RepositoryResult<Statement> result = null;
		try{
			try{
				result = this.schemaConnection.getStatements(datasetSubject, dimensions , null, false, this.schemaContext);

				Statement s;
				while(result.hasNext()){
					s = result.next();
					if(s.getObject() instanceof Literal){
						dimLabels.add(s.getObject().stringValue());
					}
				}
			}finally{
				if(result != null){
					result.close();
				}
			}
		}catch(RepositoryException e){
			logger.warn("Could not query repository for dataset dimension strings labels " + datasetIdentifier.stringValue() , e);
		}	

		return dimLabels;
	}

	@Override
	public Set<String> retrieveDimensionValueStringLabels(URI datasetIdentifier, List<URI> dimensions){
		Set<String> dimLabels = new TreeSet<String>();

		for(URI dimensionURI : dimensions){
			dimLabels.addAll(retrieveDimensionValueStringLabels(datasetIdentifier, dimensionURI));
		}

		return dimLabels;
	}


	@Override
	public Set<String> retrieveDimensionValueStringLabels(URI datasetIdentifier, URI dimension){
		Set<String> dimLabels = new TreeSet<String>();

		ValueFactory vf = this.schemaConnection.getValueFactory();
		URI datasetSubject = vf.createURI("http://eurostat.linked-statistics.org/data/", datasetIdentifier.getLocalName());
		
		StringBuilder from = new StringBuilder("");
		if(this.schemaContext != null){
			from.append(" FROM <").append(this.schemaContext.stringValue()).append("> "); 
		}

		StringBuilder query = new StringBuilder();
		query.append(" SELECT ?labels ");
		query.append(from.toString());
		query.append(" WHERE{ ");
		query.append(" <").append(datasetSubject.stringValue()).append("> <").append(dimension.stringValue()).append("> ?values . ");
		query.append(" ?values <").append(SKOS.PREFLABEL).append("> ?labels . } ");



		try{
			TupleQuery tquery = this.schemaConnection.prepareTupleQuery(QueryLanguage.SPARQL, query.toString());
			TupleQueryResult result = null;
			try{
				result = tquery.evaluate();

				BindingSet bs;
				while(result.hasNext()){
					bs = result.next();
					dimLabels.add(bs.getValue("labels").stringValue());
				}
			} finally{
				if(result != null){
					result.close();
				}
			}
		}catch(RepositoryException e){
			logger.warn("Could not query repository for dataset dimension strings labels " + datasetIdentifier.stringValue() , e);
		} catch (MalformedQueryException e) {
			logger.warn("Malformed Query when querying dimension value string labels ", e);
		} catch(QueryEvaluationException e){
			logger.warn(" Query exception when querying dimension value string labels ", e);
		}

		return dimLabels;
	}

	@Override
	public void close() {
		try{
			if(this.schemaConnection.isOpen()){
				this.schemaConnection.close();
			}
		}catch(RepositoryException e){
			logger.warn("Could not close schema connection ", e);
		}
	}
}
